use crate::app_core::App;
use config::handle;
use config::model::MysqlConfigHome;
use mysql::mysql_helper::{MysqlAccount, MysqlHelper};
use std::cell::RefCell;
use std::io::{BufRead, BufReader, BufWriter};
use std::io::{Read, Write};
use std::process::{Command, Stdio};
use std::rc::Rc;
use std::sync::Arc;
use tool::tool_date::{self, DateTime};
use tool::tool_json;
use tool::tool_result::Error;
use tool::{tool_convert, tool_io};
use tool::tool_env;
use tui_helper::handle::TuiHelper;

#[derive(Debug, Clone)]
pub enum HomePage {
    Home,
    MysqlBackUp,
}

#[derive(Debug, Clone)]
pub struct Home {
    pub page: HomePage,
    pub title: String,
    pub sub_title: String,
    pub list: Vec<String>,
    pub index: usize,

    pub home_list: Vec<String>,

    pub mysql_config_list: Vec<MysqlConfigHome>,
    pub cur_backup_mysql_config: Option<BackUpMysqlInfo>,
}

#[derive(Debug, Clone)]
pub struct BackUpMysqlInfo {
    pub mysql_config: MysqlConfigHome,
    pub cur_table: String,
    pub all_table_list: Vec<String>,
    pub ok_table_list: Vec<String>,
    pub backup_dir_path: String,
    pub zip_info: Option<ZipMysqlInfo>,
    pub stop: bool,
}

impl BackUpMysqlInfo {
    pub fn new(
        mysql_config: MysqlConfigHome,
        all_table_list: Vec<String>,
        backup_dir_path: String,
    ) -> Self {
        BackUpMysqlInfo {
            cur_table: all_table_list[0].clone(),
            mysql_config,
            all_table_list: all_table_list,
            ok_table_list: Vec::new(),
            backup_dir_path: backup_dir_path,
            zip_info: None,
            stop: false,
        }
    }
}

#[derive(Debug, Clone)]
pub struct ZipMysqlInfo {
    pub all_size: u64,
    pub cur_size: u64,
}

impl Home {
    pub fn new() -> Self {
        Home {
            page: HomePage::Home,
            title: "我是mysql备份工具(esc退出程序)".to_string(),
            sub_title: "".to_string(),
            list: vec!["开始备份".to_string(), "退出".to_string()].to_vec(),
            index: 0,

            home_list: vec!["开始备份".to_string(), "退出".to_string()].to_vec(),
            mysql_config_list: Vec::new(),
            cur_backup_mysql_config: None,
        }
    }

    pub async fn next(app: Arc<App>) {
        let mut page = HomePage::Home;
        {
            let a = app.home.read().await;
            let home_read = a;
            page = home_read.page.clone();
        }

        if let HomePage::Home = page {
            Self::next_home_home(app.clone()).await;
        }
        if let HomePage::MysqlBackUp = page {
            Self::next_home_mysql(app.clone()).await;
        }
    }

    pub async fn pre(app: Arc<App>) {
        let mut page = HomePage::Home;
        {
            let a = app.home.read().await;
            let home_read = a;
            page = home_read.page.clone();
        }
        if let HomePage::Home = page {
            Self::pre_home_home(app.clone()).await;
        }
        if let HomePage::MysqlBackUp = page {
            Self::pre_home_mysql(app.clone()).await;
        }
    }

    pub async fn next_home_home(app: Arc<App>) {
        let mut home = app.home.write().await;
        if home.index + 1 > home.list.len() - 1 {
            home.index = 0;
        } else {
            home.index += 1;
        }
    }

    pub async fn pre_home_home(app: Arc<App>) {
        let mut home = app.home.write().await;
        if home.index as i32 - 1 < 0 {
            home.index = home.list.len() - 1;
        } else {
            home.index -= 1;
        }
    }

    pub async fn next_home_mysql(app: Arc<App>) {
        let mut home = app.home.write().await;
        if home.index + 1 > home.list.len() - 1 {
            home.index = 0;
        } else {
            home.index += 1;
        }
    }

    pub async fn pre_home_mysql(app: Arc<App>) {
        let mut home = app.home.write().await;
        if home.index as i32 - 1 < 0 {
            home.index = home.list.len() - 1;
        } else {
            home.index -= 1;
        }
    }

    pub async fn change_page(app: Arc<App>, home_page: HomePage) {
        let mut home = app.home.write().await;
        if let HomePage::Home = home_page {
            home.index = 0;
            home.list = home.home_list.clone();
            home.page = HomePage::Home;
        } else if let HomePage::MysqlBackUp = home_page {
            home.index = 0;
            let mut list: Vec<String> = vec![];
            for x in home.mysql_config_list.clone() {
                list.push(x.name);
            }
            list.push("返回".to_string());
            home.list = list;
            home.page = HomePage::MysqlBackUp;
        }
    }

    pub async fn handle(app: Arc<App>) -> Result<(), Error> {
        let mut home = Home::new();
        {
            let a = app.home.read().await;
            let home_read = a;
            home = home_read.clone();
        }
        if let HomePage::Home = home.page {
            if home.index == 1 {
                //退出
                TuiHelper::shut_down();
            }
            if home.index == 0 {
                //选择备份项
                Self::load_mysql_config_list(app.clone()).await?;
                Self::change_page(app.clone(), HomePage::MysqlBackUp).await;
            }
        } else if let HomePage::MysqlBackUp = home.page {
            if home.index == home.list.len() - 1 {
                //返回
                drop(home);
                Self::change_page(app.clone(), HomePage::Home).await;
            } else {
                //开始备份
                if home.index <= home.list.len() - 1 {
                    let mut cur_backup_mysql_config = false;
                    {
                        let home = app.home.read().await;
                        cur_backup_mysql_config =
                            home.cur_backup_mysql_config.is_some();
                    }
                    if cur_backup_mysql_config {
                        App::push_msg(app.clone(), "正在备份中。。。").await;
                        return Ok(());
                    }

                    if let tool_env::OsType::Windows=tool_env::get_os(){
                        let msg_ok= tool_env::Windows::show_windows_msg_ok_and_cancel("仔细阅读", "如果你的windows系统比较旧，备份时改变cmd窗口大小可能导致程序崩溃，你可以先调整好窗口大小，确认无误后再开始备份(此警告仅在windows上显示)。");
                        if !msg_ok{
                            App::push_msg(app.clone(), "准备好再开始备份吧").await;
                            return Ok(());
                        }
                    }

                    let config = home.mysql_config_list[home.index].clone();

                    let mysql_config: MysqlConfigHome =
                        tool_json::deserialize::<MysqlConfigHome>(tool_json::serialize(config)?)?;

                    std::thread::spawn(move || {
                        tokio::runtime::Runtime::new()
                            .unwrap()
                            .block_on(Self::start_backup_mysql_handle(app.clone(), mysql_config));
                    });
                }
            }
        }
        Ok(())
    }
}

impl Home {
    pub async fn load_mysql_config_list(app: Arc<App>) -> Result<(), Error> {
        let config = match handle::get_config() {
            Ok(config) => config,
            Err(e) => {
                App::push_msg(
                    app.clone(),
                    format!("获取配置失败:{}", e.err.clone().unwrap()),
                )
                .await;
                return Err(e);
            }
        };
        let mysql_list = config.mysql_list;
        {
            let mut home = app.home.write().await;
            let mut mysql_config_home_list = Vec::new();
            for ele in mysql_list {
                mysql_config_home_list.push(ele.to_home());
            }
            home.mysql_config_list = mysql_config_home_list;
        }
        Ok(())
    }
}

impl Home {
    async fn backup_start(app: Arc<App>, backup_mysql_info: BackUpMysqlInfo) {
        {
            let mut home = app.home.write().await;
            home.cur_backup_mysql_config = Some(backup_mysql_info.clone());
            home.sub_title = format!(
                "{}备份中...        按q可终止备份",
                backup_mysql_info.mysql_config.name.clone()
            );
        }
    }

    async fn backup_over(app: Arc<App>) {
        {
            let mut home = app.home.write().await;
            home.cur_backup_mysql_config = None;
            home.sub_title = "".to_string();
        }
    }

    pub async fn stop_backup_mysql(app: Arc<App>) {
        let mut is_backup = false;
        {
            let home_read = app.home.read().await;
            if home_read
                .cur_backup_mysql_config
                .is_some()
            {
                is_backup = true;
            }
        }
        if is_backup {
            {
                let mut home_write = app.home.write().await;
                if home_write
                    .cur_backup_mysql_config
                    .is_some()
                {
                    home_write
                        .cur_backup_mysql_config
                        .as_mut()
                        .unwrap()
                        .stop = true;
                }
            }
        }
    }

    async fn start_backup_mysql_handle(app: Arc<App>, mysql_config: MysqlConfigHome) {
        let mysql = match MysqlHelper::new(MysqlAccount::new(
            mysql_config.port.clone(),
            mysql_config.host.clone(),
            mysql_config.user.clone(),
            mysql_config.password.clone(),
            mysql_config.database.clone(),
        ))
        .await
        {
            Ok(x) => x,
            Err(e) => {
                App::push_msg(app.clone(), e.err.unwrap()).await;
                return;
            }
        };
        let table_list = match mysql.ExecuteDataTable("show tables", vec![]).await {
            Ok(x) => x,
            Err(e) => {
                App::push_msg(app.clone(), e.err.unwrap()).await;
                return;
            }
        };
        let mut table_list_bak = Vec::<String>::new();
        for x in table_list {
            let keys: Vec<String> = x.keys().cloned().collect();
            let a = x.get(&keys[0]).unwrap();
            table_list_bak.push(a.to_string())
        }
        let mut table_list = table_list_bak;
        //删除不需要备份的table
        for x in (0..table_list.len()).rev() {
            if mysql_config.not_backup_table.contains(&table_list[x]) {
                table_list.remove(x);
            }
        }
        if table_list.len() == 0 {
            App::push_msg(app.clone(), "未发现可备份的表").await;
            return;
        }

        let time_str = DateTime::now().to_str();
        let full_time_str = DateTime::now().to_str_full_nospace();
        let backup_dir_path = format!(
            "{}/{}/{}/{}/file",
            "backup",
            mysql_config.name.clone(),
            time_str,
            full_time_str
        );

        let backup_dir_zip_path = format!(
            "{}/{}/{}/{}/{}.zip",
            "backup",
            mysql_config.name.clone(),
            time_str,
            full_time_str,
            full_time_str
        );

        if tool_io::exist_dir_for_file(backup_dir_path.clone()) {
            match tool_io::remove_dir_all(backup_dir_path.clone()) {
                Ok(x) => x,
                Err(e) => {
                    App::push_msg(app.clone(), e.err.unwrap()).await;
                    return;
                }
            };
        }
        match tool_io::create_dir(backup_dir_path.clone()) {
            Ok(x) => x,
            Err(e) => {
                App::push_msg(app.clone(), e.err.unwrap()).await;
                return;
            }
        };

        let mut backup_mysql_info = BackUpMysqlInfo::new(
            mysql_config.clone(),
            table_list.clone(),
            backup_dir_path.clone(),
        );

        Home::backup_start(app.clone(), backup_mysql_info.clone()).await;

        App::push_msg(app.clone(), "备份开始!".to_string()).await;

        //这里循环备份
        for x in table_list {
            {
                if app
                    .home
                    .read()
                    .await
                    .cur_backup_mysql_config
                    .as_ref()
                    .unwrap()
                    .stop
                {
                    Self::backup_over(app.clone()).await;
                    App::push_msg(app.clone(), "备份终止").await;
                    return;
                }
            }

            backup_mysql_info.cur_table = x.clone();
            {
                let mut msg_list = app.msg_list.write().await;
                let msg = format!(
                    "开始备份{}，{}/{}",
                    x.clone(),
                    backup_mysql_info.ok_table_list.len(),
                    backup_mysql_info.all_table_list.len()
                );
                msg_list.push(msg);
            }
            {
                let mut home = app.home.write().await;
                let _cur_backup_mysql_config = home.cur_backup_mysql_config.as_mut().unwrap();
                _cur_backup_mysql_config.cur_table = x.clone();
            }

            match Self::start_backup_mysql(backup_mysql_info.clone(), mysql_config.clone()).await {
                Ok(_) => {}
                Err(e) => {
                    App::push_msg(app.clone(), format!("备份失败:{}", e.err.unwrap())).await;
                    Home::backup_over(app.clone()).await;
                    return;
                }
            };
            backup_mysql_info.ok_table_list.push(x.clone());
            {
                let mut home = app.home.write().await;
                let _cur_backup_mysql_config = home.cur_backup_mysql_config.as_mut().unwrap();
                _cur_backup_mysql_config.ok_table_list.push(x.clone());
            }
        }

        App::push_msg(
            app.clone(),
            format!(
                "{}张表导出完成!  现在开始压缩...(压缩完后将会删除原文件)",
                backup_mysql_info.all_table_list.len()
            ),
        )
        .await;

        //开始压缩文件
        {
            let mut home = app.home.write().await;
            let _cur_backup_mysql_config = home.cur_backup_mysql_config.as_mut().unwrap();
            _cur_backup_mysql_config.zip_info = Some(ZipMysqlInfo {
                cur_size: 0,
                all_size: 0,
            });

            home.sub_title = format!(
                "{}备份压缩中...",
                backup_mysql_info.mysql_config.name.clone()
            );
        }
        let _app = app.clone();
        let mut zip_control = tool_io::ZipControl::new(Box::new(move |all, cur, stop| {
            let _app = _app.clone();
            Box::pin(async move {
                let mut home = _app.home.write().await;
                let _cur_backup_mysql_config = home.cur_backup_mysql_config.as_mut().unwrap();
                _cur_backup_mysql_config.zip_info.as_mut().unwrap().cur_size = cur;
                _cur_backup_mysql_config.zip_info.as_mut().unwrap().all_size = all;
            })
        }));
        match tool_io::ZipHelper::zip_dir(
            backup_dir_path.clone(),
            backup_dir_zip_path,
            &mut zip_control,
        )
        .await
        {
            Ok(_) => {}
            Err(e) => {
                App::push_msg(app.clone(), e.err.unwrap()).await;
                Home::backup_over(app.clone()).await;
                return;
            }
        };

        match tool_io::remove_dir_all(backup_dir_path) {
            Ok(_) => {}
            Err(e) => {
                App::push_msg(app.clone(), format!("删除原文件失败：{}", e.err.unwrap())).await;
                Home::backup_over(app.clone()).await;
                return;
            }
        };

        App::push_msg(app.clone(), "备份完成!").await;

        Home::backup_over(app.clone()).await;
    }

    async fn start_backup_mysql(
        backup_mysql_info: BackUpMysqlInfo,
        mysql_config: MysqlConfigHome,
    ) -> Result<(), Error> {
        let backup_file_path = format!(
            "{}/{}.sql",
            backup_mysql_info.backup_dir_path.clone(),
            backup_mysql_info.cur_table.clone()
        );
        //开始备份

        //cmd = exec.Command("docker", "exec", backupMysql.Container, "mysqldump", "-u"+backupMysql.User, "-p"+backupMysql.Password, dataBase)

        let mut tool_name = "";
        if mysql_config.is_docker {
            tool_name = "docker";
        } else {
            tool_name = "mysqldump";
        }

        // 创建 mysqldump 命令
        let mut cmd = Command::new(tool_name);

        // 构建命令参数
        let user = format!("-u{}", &mysql_config.user);
        let host = format!("-h{}", &mysql_config.host);
        let password = format!("-p{}", &mysql_config.password);

        if mysql_config.is_docker {
            cmd.arg("exec")
                .arg(&mysql_config.docker_container_name)
                .arg("mysqldump")
                .arg(user)
                .arg(host)
                .arg(password)
                .arg(mysql_config.database.clone())
                .arg(backup_mysql_info.cur_table.clone());
        } else {
            cmd.arg(user)
                .arg(host)
                .arg(password)
                .arg(mysql_config.database.clone())
                .arg(backup_mysql_info.cur_table.clone());
        }
        // 设置 stdout 为管道
        let mut child = match cmd
            .stdout(Stdio::piped())
            .stderr(Stdio::null())
            .stdin(Stdio::piped())
            .spawn()
        {
            Ok(e) => e,
            Err(e) => return Err(Error::new(e.to_string())),
        };

        // 获取 stdout
        let stdout = child.stdout.as_mut().unwrap();

        // 打开或创建备份文件，准备写入数据
        let output_file: std::fs::File = tool_io::create_file_and_return(backup_file_path)?;
        let mut writer = BufWriter::new(output_file);

        // 读取 stdout 的数据并写入文件
        let mut reader = BufReader::new(stdout);
        let mut buffer = [0; 1024];

        loop {
            let bytes_read = match reader.read(&mut buffer) {
                Ok(e) => e,
                Err(e) => return Err(Error::new(e.to_string())),
            };
            if bytes_read == 0 {
                break; // 读完数据
            }
            match writer.write_all(&buffer[..bytes_read]) {
                Ok(e) => e,
                Err(e) => return Err(Error::new(e.to_string())),
            };
        }

        // 确保所有数据都已写入
        match writer.flush() {
            Ok(e) => e,
            Err(e) => return Err(Error::new(e.to_string())),
        };

        let status = match child.wait() {
            Ok(e) => e,
            Err(e) => return Err(Error::new(e.to_string())),
        };
        //tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
        if status.success() {
            Ok(())
        } else {
            Err(Error::new("备份失败"))
        }
    }
}

impl Home {
    pub async fn time_event(app: Arc<App>) -> Result<(), Error> {
        let mut mysql_config_list = Vec::<MysqlConfigHome>::new();
        {
            let home = app.home.read().await;
            if home.cur_backup_mysql_config.is_some() {
                return Ok(());
            }
            mysql_config_list = home.mysql_config_list.clone();
        }
        if mysql_config_list.len() != 0 {
            for (i, x) in mysql_config_list.iter().enumerate() {
                if x.auto_backup_time == "" {
                    continue;
                }

                if x.today_atuo_backup {
                    App::push_msg(app.clone(), "今天已经备份").await;
                    continue;
                }

                //备份时间
                let (mysql_hour, mysql_minute) = x.get_auto_backup_time()?;

                //系统本地时间
                let (local_hour, local_minute) = tool_date::DateTime::get_hour_minute();

                if mysql_hour == local_hour && mysql_minute == local_minute {
                    //开始执行自动备份
                    let mut cur_backup_mysql_config = false;
                    {
                        let home = app.home.read().await;
                        cur_backup_mysql_config =
                            home.cur_backup_mysql_config.is_some();
                    }
                    if cur_backup_mysql_config {
                        App::push_msg(app.clone(), "正在备份中。。。").await;
                        return Ok(());
                    }
                    {
                        let mut home = app.home.write().await;
                        home.mysql_config_list[i].today_atuo_backup = true;
                    }
                    App::push_msg(app.clone(), "开始......").await;
                    // std::thread::spawn(move || {
                    //     tokio::runtime::Runtime::new()
                    //         .unwrap()
                    //         .block_on(Self::start_backup_mysql_handle(app.clone(), x));
                    // });
                    //
                    break;
                }
            }
        }
        Ok(())
    }
}
